package com.yunwaikeji.gy_tool_library.widget.camera;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.YuvImage;
import android.graphics.drawable.Drawable;
import android.hardware.Camera;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.yunwaikeji.gy_tool_library.R;
import com.yunwaikeji.gy_tool_library.interface1.GYViewBgInterface;
import com.yunwaikeji.gy_tool_library.interface1.GYViewSetInterface;
import com.yunwaikeji.gy_tool_library.set.GYViewBgBuilder;
import com.yunwaikeji.gy_tool_library.set.GYViewSetSet;
import com.yunwaikeji.gy_tool_library.utils.display.GYDisplayUtils;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class GYCameraView extends SurfaceView implements GYCameraInfo, GYViewSetInterface<GYCameraView, GYCameraView.GYViewSet>, GYViewBgInterface<GYCameraView, GYCameraView.GYViewBgBuilder1>, SurfaceHolder.Callback, Camera.PreviewCallback {
    public GYCameraView(Context context) {
        super(context);
        initView(null);
    }

    public GYCameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(attrs);
    }

    public GYCameraView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public GYCameraView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView(attrs);
    }

    private int cameraLens = CameraLens.BACK;//摄像头选择，前置或后置
    private int previewMode = PreviewMode.NORMAL;//预览模式，NONE不展示预览，NORMAL正常预览，FACE人脸检测
    private float maxWidth = WRAP_CONTENT;//最大宽度
    private float maxHeight = WRAP_CONTENT;//最大高度
    private boolean previewShow = true;//是否预览
    private int faceGravity = FaceGravity.LEFT_TOP;//人脸识别区域所在父布局的位置
    private float faceWidth = MATCH_PARENT;//人脸检测宽度
    private float faceHeight = MATCH_PARENT;//人脸检测高度
    private float faceMarginLeft = 0;//人脸左边距
    private float faceMarginTop = 0;//人脸上边距
    private float faceMarginRight = 0;//人脸右边距
    private float faceMarginBottom = 0;//人脸下边距
    private int resolutionMaxWidth = WRAP_CONTENT;//分辨率最大宽度
    private int resolutionMaxHeight = WRAP_CONTENT;//分辨率最大高度
    private int displayOrientation = 90;//摄像头旋转角度
    private boolean faceOval = false;//人脸边框是否椭圆
    private Drawable maskground = null;//遮罩层背景

    @Override
    public void initView(AttributeSet attributeSet) {
        float faceMargin = WRAP_CONTENT;//人脸边距
        if (attributeSet != null) {
            TypedArray typedArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.GYCameraView);
            cameraLens = typedArray.getInteger(R.styleable.GYCameraView_cameraLens, cameraLens);
            previewMode = typedArray.getInteger(R.styleable.GYCameraView_previewMode, previewMode);
            maxWidth = typedArray.getDimension(R.styleable.GYCameraView_android_maxWidth, maxWidth);
            maxHeight = typedArray.getDimension(R.styleable.GYCameraView_android_maxHeight, maxHeight);
            previewShow = typedArray.getBoolean(R.styleable.GYCameraView_previewShow, previewShow);
            faceGravity = typedArray.getInteger(R.styleable.GYCameraView_faceGravity, faceGravity);
            faceWidth = typedArray.getDimension(R.styleable.GYCameraView_faceWidth, faceWidth);
            faceHeight = typedArray.getDimension(R.styleable.GYCameraView_faceHeight, faceHeight);
            faceMargin = typedArray.getDimension(R.styleable.GYCameraView_faceMargin, faceMargin);
            faceMarginLeft = typedArray.getDimension(R.styleable.GYCameraView_faceMarginLeft, faceMarginLeft);
            faceMarginTop = typedArray.getDimension(R.styleable.GYCameraView_faceMarginTop, faceMarginTop);
            faceMarginRight = typedArray.getDimension(R.styleable.GYCameraView_faceMarginRight, faceMarginRight);
            faceMarginBottom = typedArray.getDimension(R.styleable.GYCameraView_faceMarginBottom, faceMarginBottom);
            resolutionMaxWidth = typedArray.getInt(R.styleable.GYCameraView_resolutionMaxWidth, resolutionMaxWidth);
            resolutionMaxHeight = typedArray.getInt(R.styleable.GYCameraView_resolutionMaxHeight, resolutionMaxHeight);
            displayOrientation = typedArray.getInt(R.styleable.GYCameraView_displayOrientation, displayOrientation);
            maskground = typedArray.getDrawable(R.styleable.GYCameraView_maskground);
            faceOval = typedArray.getBoolean(R.styleable.GYCameraView_faceOval, faceOval);
            typedArray.recycle();
        }
        if (faceMargin >= 0) {
            faceMarginLeft = faceMargin;
            faceMarginTop = faceMargin;
            faceMarginRight = faceMargin;
            faceMarginBottom = faceMargin;
        }
        if (faceWidth < 0 && faceWidth != MATCH_PARENT) {
            faceWidth = 0;
        }
        if (faceHeight < 0 && faceHeight != MATCH_PARENT) {
            faceHeight = 0;
        }
        getBackgroundBuilder(attributeSet).build();
        getHolder().addCallback(this);
    }

    private GYViewBgBuilder1 viewBgBuilder1;

    @Override
    public GYViewBgBuilder1 setBackgroundBuilder(GYViewBgBuilder1 gyViewBgBuilder1) {
        viewBgBuilder1 = gyViewBgBuilder1;
        return viewBgBuilder1;
    }

    @Override
    public GYViewBgBuilder1 getBackgroundBuilder() {
        return getBackgroundBuilder(null);
    }

    @Override
    public GYViewBgBuilder1 getBackgroundBuilder(AttributeSet attributeSet) {
        if (viewBgBuilder1 == null) {
            viewBgBuilder1 = new GYViewBgBuilder1(this, attributeSet);
        }
        return viewBgBuilder1;
    }

    private GYViewSet viewSet;

    @Override
    public GYViewSet getViewSet() {
        if (viewSet == null) {
            viewSet = new GYViewSet(this);
        }
        return viewSet;
    }

    public static class GYViewSet extends GYViewSetSet<GYCameraView, GYViewSet> {
        public GYViewSet(View view) {
            super(view);
        }
    }

    public static class GYViewBgBuilder1 extends GYViewBgBuilder<GYCameraView, GYViewBgBuilder1> {
        public GYViewBgBuilder1(View view, AttributeSet attributeSet) {
            super(view, attributeSet);
        }

        public GYViewBgBuilder1(View view) {
            super(view);
        }
    }

    /**
     * 摄像头最大的预览尺寸
     */
    private Camera.Size previewMaxSize;
    /**
     * 预览时Frame的计数器
     */
    private int frameCount;
    /**
     * 是否正在检测人脸
     */
    private boolean detectingFace = false;

    /**
     * 是否已检测到人脸
     */
    private boolean detectedFace = false;

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        try {
            switch (cameraLens) {
                case CameraLens.BACK:
                    camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
                    break;
                case CameraLens.FRONT:
                    camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
                    break;
            }
            if (camera != null) {
                camera.setDisplayOrientation(displayOrientation);
                camera.setPreviewDisplay(holder);
                camera.setPreviewCallback(this);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
        if (camera != null) {
            previewMaxSize = getPreviewMaxSize();
            if (previewMaxSize != null) {
                Camera.Parameters parameters = camera.getParameters();
                parameters.setPreviewSize(previewMaxSize.width, previewMaxSize.height);
                camera.setParameters(parameters);
            }
            if (previewShow) {
                startPreview();
            }
            frameCount = 0;
            detectedFace = false;
        }
    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
        if (camera != null) {
            try {
                stopPreview();
                camera.setPreviewDisplay(null);
                camera.setPreviewCallback(null);
                camera.release();
                camera = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        frameCount++;
        if (previewMode == PreviewMode.FACE && frameCount > 15 && !detectingFace) {
            detectingFace = true;
            new Thread() {
                @Override
                public void run() {
                    Camera.Size size = camera.getParameters().getPreviewSize();
                    int left = (int) (size.height * faceLeft / getWidth());
                    int top = (int) (size.width * faceTop / getHeight());
                    int right = (int) (size.height * faceRight / getWidth());
                    int bottom = (int) (size.width * faceBottom / getHeight());
                    YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    yuvImage.compressToJpeg(new Rect(left, top, right, bottom), 100, byteArrayOutputStream);
                    byte[] bytes = byteArrayOutputStream.toByteArray();
                    detectFaces(bytes);
                }
            }.start();
        }
    }

    private float faceLeft = 0;
    private float faceTop = 0;
    private float faceRight = 0;
    private float faceBottom = 0;

    private void initFace() {
        if (faceWidth == MATCH_PARENT) {
            faceWidth = getWidth();
        }
        if (faceHeight == MATCH_PARENT) {
            faceHeight = getHeight();
        }
        faceLeft = 0;
        faceTop = 0;
        float xOffset = faceMarginLeft - faceMarginRight;
        float yOffset = faceMarginTop - faceMarginBottom;
        final float xCenter = (getWidth() - faceWidth) / 2 + xOffset;
        final float yCenter = (getHeight() - faceHeight) / 2 + yOffset;
        switch (faceGravity) {
            case FaceGravity.LEFT_TOP:
                faceLeft = xOffset;
                faceTop = yOffset;
                break;
            case FaceGravity.TOP_CENTER:
                faceLeft = xCenter;
                faceTop = yOffset;
                break;
            case FaceGravity.RIGHT_TOP:
                faceLeft = getWidth() - faceWidth + xOffset;
                faceTop = yOffset;
                break;
            case FaceGravity.RIGHT_CENTER:
                faceLeft = getWidth() - faceWidth + xOffset;
                faceTop = yCenter;
                break;
            case FaceGravity.RIGHT_BOTTOM:
                faceLeft = getWidth() - faceWidth + xOffset;
                faceTop = getHeight() - faceHeight + yOffset;
                break;
            case FaceGravity.BOTTOM_CENTER:
                faceLeft = xCenter;
                faceTop = getHeight() - faceHeight + yOffset;
                break;
            case FaceGravity.LEFT_BOTTOM:
                faceLeft = xOffset;
                faceTop = getHeight() - faceHeight + yOffset;
                break;
            case FaceGravity.LEFT_CENTER:
                faceLeft = xOffset;
                faceTop = yCenter;
                break;
            case FaceGravity.CENTER:
                faceLeft = xCenter;
                faceTop = yCenter;
                break;
        }
        faceRight = faceLeft + faceWidth;
        faceBottom = faceTop + faceHeight;
    }

    private Camera camera;

    public GYCameraView startPreview() {
        if (camera != null) {
            previewShow = true;
            detectingFace = false;
            detectedFace = false;
            camera.startPreview();
        }
        return this;
    }

    public boolean isPreviewShow() {
        return previewShow;
    }

    public GYCameraView stopPreview() {
        if (camera != null) {
            previewShow = false;
            camera.stopPreview();
        }
        return this;
    }

    public Camera.Parameters getParameters() {
        return camera.getParameters();
    }

    public Camera getCamera() {
        return camera;
    }

    public GYCameraView setResolutionMaxWidth(int resolutionMaxWidth) {
        this.resolutionMaxWidth = resolutionMaxWidth;
        return this;
    }

    public GYCameraView setResolutionMaxHeight(int resolutionMaxHeight) {
        this.resolutionMaxHeight = resolutionMaxHeight;
        return this;
    }

    private Camera.Size getPreviewMaxSize() {
        List<Camera.Size> allSizeList = camera.getParameters().getSupportedPreviewSizes();
        if (allSizeList == null || allSizeList.size() == 0) {
            return null;
        }
        Collections.sort(allSizeList, new Comparator<Camera.Size>() {
            @Override
            public int compare(Camera.Size o1, Camera.Size o2) {
                return o2.width * o2.height - o1.width * o1.height;
            }
        });
        if (getWidth() > maxWidth || getHeight() > maxHeight) {
            ViewGroup.LayoutParams params = getLayoutParams();
            params.width = (int) Math.min(getWidth(), maxWidth);
            params.height = (int) Math.min(getHeight(), maxHeight);
            setLayoutParams(params);
        }
        for (Camera.Size size : allSizeList) {
            Log.e("GYCameraView", size.width + "  " + size.height);
        }
        if (resolutionMaxWidth >= 0 || resolutionMaxHeight >= 0) {
            for (Camera.Size size : allSizeList) {
                int width = size.height;
                int height = size.width;
                if (resolutionMaxWidth >= 0 && resolutionMaxHeight >= 0) {
                    if (width <= resolutionMaxWidth && height <= resolutionMaxHeight) {
                        return size;
                    }
                } else if (resolutionMaxWidth >= 0) {
                    if (width <= resolutionMaxWidth) {
                        return size;
                    }
                } else {
                    if (height <= resolutionMaxHeight) {
                        return size;
                    }
                }
            }
        }
        return allSizeList.get(0);
    }

    /**
     * 检测data数据中是否有人脸，这里需要先旋转一下图片，该方法执行在子线程中
     *
     * @param data
     */
    private void detectFaces(byte[] data) {
        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        Bitmap bitmap1 = GYDisplayUtils.newBuilder()
                .setNormal(bitmap)
                .setRotateDegree(90)
                .setFlip(true)
                .buildBitmap();
        Bitmap bitmap2 = bitmap1.copy(Bitmap.Config.RGB_565, true);
        if (onReviewListener != null) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    onReviewListener.onReview(bitmap2);
                }
            });
        }
        int faceNumber = GYDisplayUtils.getFaceNumber(bitmap2);
        if (faceNumber > 0) {
            if (!detectedFace) {
                if (onFaceDetectedListener != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (onFaceDetectedListener.onFaceDetected(bitmap2)) {
                                detectedFace = true;
                                stopPreview();
                            }
                        }
                    });
                }
            }
        }
        detectingFace = false;
    }

    private final Handler handler = new Handler(Looper.getMainLooper());

    private OnFaceDetectedListener onFaceDetectedListener;

    /**
     * 设置监听器，监听检测到人脸的动作
     */
    public GYCameraView setOnFaceDetectedListener(OnFaceDetectedListener onFaceDetectedListener) {
        this.onFaceDetectedListener = onFaceDetectedListener;
        return this;
    }

    private OnReviewListener onReviewListener;

    public GYCameraView setOnReviewListener(OnReviewListener onReviewListener) {
        this.onReviewListener = onReviewListener;
        return this;
    }

    private void showError(String error) {
        Log.e("GYCameraView", error);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        initFace();

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setAntiAlias(true);
        paint.setColor(PAINT_NORMAL_COLOR);

        Bitmap maskBmp = null;
        if (maskground != null) {
            maskBmp = GYDisplayUtils.toBitmap(maskground, getWidth(), getHeight());
        }

        if (maskBmp != null) {
            canvas.drawBitmap(maskBmp, 0, 0, paint);
        }
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        RectF faceRectF = new RectF(faceLeft, faceTop, faceRight, faceBottom);
        if (faceOval) {
            canvas.drawOval(faceRectF, paint);
        } else {
            canvas.drawRect(faceRectF, paint);
        }
    }
}
